﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections.Specialized;
using System.Web;
using PagSeguro.Mvc.Extensions;
using PagSeguro.Mvc.Recursos;

namespace PagSeguro.Mvc
{
    /// <summary>
    /// Dados da transação.
    /// </summary>
    public class Transacao
    {
        #region PROPRIEDADES

        /// <summary>
        /// Get do E-mail do vendedor.
        /// </summary>
        /// <remarks>
        /// VendedorEmail
        /// TIPO......: Alfa-numérico
        /// TAMANHO...: 255
        /// DESCRIÇÃO.: Seu e-mail.
        /// </remarks>
        public string EmailDoVendedor { get; private set; }

        /// <summary>
        /// Get do identificador da transação.
        /// </summary>
        /// <remarks>
        /// TransacaoID
        /// TIPO......: Alfa-numérico
        /// TAMANHO...: 35
        /// DESCRIÇÃO.: Identificação da Transação.
        /// </remarks>
        public string TransacaoID { get; private set; }

        /// <summary>
        /// Get do número de referência usado como identificador em seu site.
        /// </summary>
        /// <remarks>
        /// Referencia
        /// TIPO......: Alfa-numérico
        /// TAMANHO...: 255
        /// DESCRIÇÃO.: Número de referência gerado por você e enviado ao 
        ///             PagSeguro para identificação em seu site.
        /// </remarks>
        public string Referencia { get; private set; }

        /// <summary>
        /// Get do valor da taxa extra.
        /// </summary>
        /// <remarks>
        /// Extras
        /// TIPO......: Decimal
        /// TAMANHO...: 10,2
        /// DESCRIÇÃO.: Valor de taxas extras. Formato: 0,00.
        /// </remarks>
        public decimal Extras { get; private set; }

        /// <summary>
        /// Get do tipo do frete.
        /// </summary>
        /// <remarks>
        /// TipoFrete
        /// TIPO......: Alfa-numérico
        /// TAMANHO...: 2
        /// DESCRIÇÃO.: Tipo de frete pago pelo cliente. Pode receber as seguintes variáveis:
        ///             - SD: Sedex
        ///             - EN: Encomenda econômica (PAC)
        /// </remarks>
        public TipoFrete TipoDoFrete { get; private set; }

        /// <summary>
        /// Get a descrição do tipo do frete.
        /// </summary>
        public string DescricaoDoTipoDoFrete 
        { 
            get { return this.TipoDoFrete.Descricao(); } 
        }

        /// <summary>
        /// Get do valor total do frete.
        /// </summary>
        /// <remarks>
        /// ValorFrete
        /// TIPO......: Alfa-numérico
        /// TAMANHO...: 2
        /// DESCRIÇÃO.: Valor total do frete pago pelo cliente. 
        ///             Formato: 0,00.
        /// </remarks>
        public decimal ValorTotalDoFrete { get; private set; }

        /// <summary>
        /// Get da anotação.
        /// </summary>
        /// <remarks>
        /// Anotacao
        /// TIPO......: Alfa-numérico
        /// TAMANHO...: 250
        /// DESCRIÇÃO.: Anotação enviada pelo cliente.
        /// </remarks>
        public string Anotacao { get; private set; }

        /// <summary>
        /// Get da data de transação.
        /// </summary>
        /// <remarks>
        /// DataTransacao
        /// TIPO......: Data
        /// TAMANHO...: -
        /// DESCRIÇÃO.: Data e hora da transação. 
        ///             Formato: dd/mm/yyyy hh:mm:ss.
        /// </remarks>
        public DateTime DataTransacao { get; private set; }

        /// <summary>
        /// Get do tipo do pagamento realizado.
        /// </summary>
        /// <remarks>
        /// TipoPagamento
        /// TIPO......: Alfa-numérico
        /// TAMANHO...: 30
        /// DESCRIÇÃO.: Tipo de pagamento efetuado pelo cliente. Pode receber as seguintes variáveis:
        ///             - Pagamento: Pagamento PagSeguro
        ///             - Cartão de Crédito: Pagamento via cartão de crédito
        ///             - Boleto: Pagamento via boleto bancário
        ///             - Pagamento Online: Pagamento via transferência online
        /// </remarks>
        public TipoPagamento TipoDoPagamento { get; private set; }

        /// <summary>
        /// Get a descrição do tipo do pagamento.
        /// </summary>
        public string DescricaoDoTipoDoPagamento 
        { 
            get { return this.TipoDoPagamento.Descricao(); } 
        }

        /// <summary>
        /// Get do status da transação.
        /// </summary>
        /// <remarks>
        /// StatusTransacao
        /// TIPO......: Alfa-numérico
        /// TAMANHO...: 30
        /// DESCRIÇÃO.: Status da transação efetuada. Pode receber as seguintes variáveis:
        ///             - Completo: Pagamento completo
        ///             - Aguardando Pagto: Aguardando pagamento do cliente
        ///             - Aprovado: Pagamento aprovado, aguardando compensação
        ///             - Em Análise: Pagamento aprovado, em análise pelo PagSeguro
        ///             - Cancelado: Pagamento cancelado pelo PagSeguro
        /// </remarks>
        public StatusTransacao StatusDaTransacao { get; private set; }

        /// <summary>
        /// Get a descrição do status da transação.
        /// </summary>
        public string DescricaoDoStatusDaTransacao 
        {
            get { return this.StatusDaTransacao.Descricao(); } 
        }

        /// <summary>
        /// Get das parcelas da compra.
        /// </summary>
        /// <remarks>
        /// Parcelas
        /// TIPO......: Numérico
        /// TAMANHO...: 2
        /// DESCRIÇÃO.: 
        /// Quantidade de parcelas do pagamento. 
        /// Para pagamentos à vista (TEF, boleto e Saldo PagSeguro) a quantidade de parcelas informada sempre será 1.
        /// </remarks>
        public int Parcelas { get; private set; }

        /// <summary>
        /// Get do valor total da compra. (Total de todos os produtos com frete)
        /// </summary>
        public decimal Total
        {
            get { return _produtos.Sum(x => x.PrecoTotal); }
        }

        /// <summary>
        /// Get do valor total da compra sem a soma do valor do frete. 
        /// (Total de todos os produtos sem frete)
        /// </summary>
        public decimal TotalSemFrete
        {
            get { return _produtos.Sum(x => x.PrecoTotalSemFrete); }
        }

        /// <summary>
        /// Get do valor total da compra. 
        /// (Total de todos os produtos + valor da taxa extra)
        /// </summary>
        public decimal TotalComTaxaExtra 
        { 
            get { return (Total + Extras); } 
        }

        /// <summary>
        /// Get do valor total da compra somado ao valor da taxa sem a soma do valor do frete. 
        /// (Total de todos os produtos + valor da taxa extra - valor do frete)
        /// </summary>
        public decimal TotalComTaxaExtraSemFrete
        {
            get { return (TotalSemFrete + Extras); }
        }

        /// <summary>
        /// Get do cliente que efetuou a compra.
        /// </summary>
        public Cliente Cliente { get; private set; }

        /// <summary>
        /// Get dos produtos vendidos.
        /// </summary>
        private List<Produto> _produtos;
        public IEnumerable<Produto> Produtos
        {
            get { return _produtos = (_produtos ?? new List<Produto>()); }
        }

        #endregion

        /// <summary>
        /// Construtor padrão.
        /// </summary>
        /// <param name="dados">Requisição enviada pelo PagSeguro.</param>
        internal Transacao(NameValueCollection dados) 
        {
            // TODO: Validação das chaves comentadas temporariamente
            //ValidarDadosPost(dados);

            this.EmailDoVendedor = dados[Constantes.VENDEDOR_EMAIL];
            this.TransacaoID = dados[Constantes.TRANSACAO_ID];
            this.Referencia = dados[Constantes.REFERENCIA];
            this.Extras = dados[Constantes.EXTRAS].ToDecimal();
            this.ValorTotalDoFrete = dados[Constantes.VALOR_FRETE].ToDecimal();
            this.Anotacao = dados[Constantes.ANOTACAO];
            this.DataTransacao = dados[Constantes.DATA_TRANSACAO].ToDateTime();
            this.Parcelas = dados[Constantes.PARCELAS].ToInt32();

            this.MontarTipoFrete(dados[Constantes.TIPO_FRETE]);
            this.MontarTipoPagamento(dados[Constantes.TIPO_PAGAMENTO]);
            this.MontarStatusTransacao(dados[Constantes.STATUS_TRANSACAO]);

            // Cliente
            this.Cliente = new Cliente(dados);
            
            // Produtos
            this._produtos = Produto.MontarProdutos(dados);
        }

        /// <summary>
        /// Monta o enumerador do tipo frete.
        /// </summary>
        /// <param name="tipoFrete">Tipo do frete em formato string.</param>
        private void MontarTipoFrete(string tipoFrete)
        {
            switch (tipoFrete)
            {
                case "SD":
                    this.TipoDoFrete = TipoFrete.Sedex;
                    break;
                case "EN":
                    this.TipoDoFrete = TipoFrete.EncomendaEconomica;
                    break;
                default:
                    this.TipoDoFrete = TipoFrete.Nenhum;
                    break;
            }
        }

        /// <summary>
        /// Monta o enumerador do tipo pagamento.
        /// </summary>
        /// <param name="tipoPagamento">Tipo do pagamento em formato string.</param>
        private void MontarTipoPagamento(string tipoPagamento)
        {
            switch (tipoPagamento)
            {
                case "Pagamento":
                    this.TipoDoPagamento = TipoPagamento.Pagamento;
                    break;
                case "Cartão de Crédito":
                    this.TipoDoPagamento = TipoPagamento.CartaoDeCredito;
                    break;
                case "Boleto":
                    this.TipoDoPagamento = TipoPagamento.Boleto;
                    break;
                case "Pagamento Online":
                    this.TipoDoPagamento = TipoPagamento.PagamentoOnline;
                    break;
                default:
                    this.TipoDoPagamento = TipoPagamento.Nenhum;
                    break;
            }
        }

        /// <summary>
        /// Monta o enumerador do status da transação.
        /// </summary>
        /// <param name="statusTransacao">Status da transação em formato string.</param>
        private void MontarStatusTransacao(string statusTransacao)
        {
            switch (statusTransacao)
            {
                case "Completo":
                    this.StatusDaTransacao = StatusTransacao.Completo;
                    break;
                case "Aguardando Pagto":
                    this.StatusDaTransacao = StatusTransacao.AguardandoPagamento;
                    break;
                case "Aprovado":
                    this.StatusDaTransacao = StatusTransacao.Aprovado;
                    break;
                case "Em Análise":
                    this.StatusDaTransacao = StatusTransacao.EmAnalise;
                    break;
                case "Cancelado":
                    this.StatusDaTransacao = StatusTransacao.Cancelado;
                    break;
                default:
                    this.StatusDaTransacao = StatusTransacao.Nenhum;
                    break;
            }
        }

        /// <summary>
        /// Valida as chaves do dicionário
        /// </summary>
        /// <param name="dados">Dados do cliente.</param>
        private static void ValidarDadosPost(NameValueCollection dados)
        {
            dados.Validar(Constantes.VENDEDOR_EMAIL);
            dados.Validar(Constantes.TRANSACAO_ID);
            dados.Validar(Constantes.REFERENCIA);
            dados.Validar(Constantes.EXTRAS);
            dados.Validar(Constantes.VALOR_FRETE);
            dados.Validar(Constantes.ANOTACAO);
            dados.Validar(Constantes.DATA_TRANSACAO);
            dados.Validar(Constantes.TIPO_FRETE);
            dados.Validar(Constantes.TIPO_PAGAMENTO);
            dados.Validar(Constantes.STATUS_TRANSACAO);
            dados.Validar(Constantes.PARCELAS);
        }

        /// <summary>
        /// Breve descrição do status da transação
        /// </summary>
        /// <returns>ID e status</returns>
        public override string ToString()
        {
            return String.Format("Transação {0} com status: {1}", TransacaoID, StatusDaTransacao.Descricao());
        }
    }
}
